<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js coal">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Swift regular exressions - Andrew&#x27;s Blog</title>


        <!-- Custom HTML head -->
        
        <meta name="description" content="Andrew Ryan&#x27;s Blog">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
        <link rel="stylesheet" href="../../src/style/custom.css">

        <!-- MathJax -->
        <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    </head>
    <body>
    <div id="body-container">
        <!-- Provide site root to javascript -->
        <script>
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "coal" : "coal";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('coal')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            var html = document.querySelector('html');
            var sidebar = null;
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            } else {
                sidebar = 'hidden';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item affix "><a href="../../index.html">Andrew's Blog</a></li><li class="chapter-item "><a href="../../posts/linux/linux.html"><strong aria-hidden="true">1.</strong> Linux</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/linux/install_linux.html"><strong aria-hidden="true">1.1.</strong> install linux</a></li><li class="chapter-item "><a href="../../posts/linux/bash_profile.html"><strong aria-hidden="true">1.2.</strong> bash profile</a></li><li class="chapter-item "><a href="../../posts/linux/command_list.html"><strong aria-hidden="true">1.3.</strong> command list</a></li><li class="chapter-item "><a href="../../posts/linux/git_guide.html"><strong aria-hidden="true">1.4.</strong> git guide</a></li><li class="chapter-item "><a href="../../posts/linux/tar.html"><strong aria-hidden="true">1.5.</strong> tar</a></li><li class="chapter-item "><a href="../../posts/Linux/git_cheatsheet.html"><strong aria-hidden="true">1.6.</strong> Git Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/Linux/bash_cheatsheet.html"><strong aria-hidden="true">1.7.</strong> Bash Cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/macos/mac.html"><strong aria-hidden="true">2.</strong> MacOS</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/macos/macos_profiles.html"><strong aria-hidden="true">2.1.</strong> macos profiles</a></li><li class="chapter-item "><a href="../../posts/macos/macos_pwn_env_setup.html"><strong aria-hidden="true">2.2.</strong> macos pwn env setup</a></li></ol></li><li class="chapter-item expanded "><a href="../../posts/swift/swift.html"><strong aria-hidden="true">3.</strong> Swift</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/swift/learn_swift.html"><strong aria-hidden="true">3.1.</strong> learn swift basics</a></li><li class="chapter-item "><a href="../../posts/swift/swift_extensions.html"><strong aria-hidden="true">3.2.</strong> Swift extensions</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_extension.html"><strong aria-hidden="true">3.3.</strong> SwiftUI extensions</a></li><li class="chapter-item "><a href="../../posts/swift/install_swift.html"><strong aria-hidden="true">3.4.</strong> install swift</a></li><li class="chapter-item "><a href="../../posts/swift/task_planner.html"><strong aria-hidden="true">3.5.</strong> implment task panner app with SwiftUI</a></li><li class="chapter-item "><a href="../../posts/swift/swift_cheat_sheet.html"><strong aria-hidden="true">3.6.</strong> Swift Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/swift/yinci_url.html"><strong aria-hidden="true">3.7.</strong> Personal privacy protocol</a></li><li class="chapter-item expanded "><a href="../../posts/swift/swift_regular_exressions.html" class="active"><strong aria-hidden="true">3.8.</strong> Swift regular exressions</a></li><li class="chapter-item "><a href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html"><strong aria-hidden="true">3.9.</strong> How to Create Beautiful iOS Charts in Swift</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_source_code.html"><strong aria-hidden="true">3.10.</strong> SwiftUI source code</a></li><li class="chapter-item "><a href="../../posts/swift/use_swift_fetch_iciba_api.html"><strong aria-hidden="true">3.11.</strong> use swift fetch iciba API</a></li></ol></li><li class="chapter-item "><a href="../../posts/ios/ios.html"><strong aria-hidden="true">4.</strong> iOS</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ios/cocaposd_setup_and_install_for_ios_project.html"><strong aria-hidden="true">4.1.</strong> cocaposd setup and install for ios project</a></li><li class="chapter-item "><a href="../../posts/ios/swiftui_show_gif_image.html"><strong aria-hidden="true">4.2.</strong> SwiftUI show gif image</a></li><li class="chapter-item "><a href="../../posts/ios/implement_task_planner_app.html"><strong aria-hidden="true">4.3.</strong> implement Task planner App</a></li></ol></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c.html"><strong aria-hidden="true">5.</strong> Objective-C</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/objective_c/objective_c_cheat_sheet.html"><strong aria-hidden="true">5.1.</strong> Objective-C Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c_for_absolute_beginners_read_note.html"><strong aria-hidden="true">5.2.</strong> Objective-C Note</a></li></ol></li><li class="chapter-item "><a href="../../posts/dart/dart.html"><strong aria-hidden="true">6.</strong> Dart</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/dart/flutter.html"><strong aria-hidden="true">6.1.</strong> Flutter Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/dart/dart_cheat_sheet.html"><strong aria-hidden="true">6.2.</strong> Dart Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/flutter/flutter_dev_test.html"><strong aria-hidden="true">6.3.</strong> Flutter dev test</a></li></ol></li><li class="chapter-item "><a href="../../posts/rust/rust.html"><strong aria-hidden="true">7.</strong> Rust</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/rust/offline_use_rust.html"><strong aria-hidden="true">7.1.</strong> Offline use rust</a></li><li class="chapter-item "><a href="../../posts/rust/rust_grammer.html"><strong aria-hidden="true">7.2.</strong> rust grammar</a></li><li class="chapter-item "><a href="../../posts/rust/pase_string_and_decimal_conversion.html"><strong aria-hidden="true">7.3.</strong> pase string and decimal conversion</a></li><li class="chapter-item "><a href="../../posts/rust/parse_types.html"><strong aria-hidden="true">7.4.</strong> rust types</a></li><li class="chapter-item "><a href="../../posts/rust/rust_life_cycle.html"><strong aria-hidden="true">7.5.</strong> Rust life cycle</a></li><li class="chapter-item "><a href="../../posts/rust/rust_generic.html"><strong aria-hidden="true">7.6.</strong> rust generics</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implment_matrix.html"><strong aria-hidden="true">7.7.</strong> Rust implement matrix</a></li><li class="chapter-item "><a href="../../posts/rust/rust_sort.html"><strong aria-hidden="true">7.8.</strong> Rust implement sort algorithms</a></li><li class="chapter-item "><a href="../../posts/rust/implement_aes_encryption.html"><strong aria-hidden="true">7.9.</strong> Rust implement AEC encryption and decryption</a></li><li class="chapter-item "><a href="../../posts/rust/implement_trie_data_structure.html"><strong aria-hidden="true">7.10.</strong> implement trie data structure</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_tree.html"><strong aria-hidden="true">7.11.</strong> implement tree data_structure</a></li><li class="chapter-item "><a href="../../posts/rust/list_dir.html"><strong aria-hidden="true">7.12.</strong> list dir</a></li><li class="chapter-item "><a href="../../posts/rust/fast_way_to_implment_object_trait.html"><strong aria-hidden="true">7.13.</strong> fast way to implment object trait</a></li><li class="chapter-item "><a href="../../posts/rust/compress_rust_binary_size.html"><strong aria-hidden="true">7.14.</strong> compress rust binary size</a></li><li class="chapter-item "><a href="../../posts/rust/implment_file_upload_backend.html"><strong aria-hidden="true">7.15.</strong> impliment file upload</a></li><li class="chapter-item "><a href="../../posts/rust/this_is_add_post_cli_implementation_in_rust.html"><strong aria-hidden="true">7.16.</strong> this is add_post cli implementation in rust</a></li><li class="chapter-item "><a href="../../posts/rust/use_rust_implment_a_copyclipbord_cli.html"><strong aria-hidden="true">7.17.</strong> Use rust implment a copyclipbord CLI</a></li><li class="chapter-item "><a href="../../posts/rust/sqlite_database_add_delete_update_show_in_rust.html"><strong aria-hidden="true">7.18.</strong> sqlite database add delete update show in rust</a></li><li class="chapter-item "><a href="../../posts/rust/implementing_tokio_joinhandle_for_wasm.html"><strong aria-hidden="true">7.19.</strong> Implementing tokio JoinHandle for wasm</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_a_crate_for_encode_and_decode_brainfuck_and_ook.html"><strong aria-hidden="true">7.20.</strong> rust implement a crate for encode and decode brainfuck and ook</a></li><li class="chapter-item "><a href="../../posts/rust/slint_builtin_elements.html"><strong aria-hidden="true">7.21.</strong> Slint Builtin Elements</a></li><li class="chapter-item "><a href="../../posts/rust/corporate_network_install_rust_on_windows.html"><strong aria-hidden="true">7.22.</strong> Corporate network install Rust on windows</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_file_how_to_judge_static_link_or_dynamic_link_in_macos.html"><strong aria-hidden="true">7.23.</strong> rust binary file how to judge static link or dynamic link in Macos</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_include_dir_and_get_contents.html"><strong aria-hidden="true">7.24.</strong> rust binary include dir and get contents</a></li><li class="chapter-item "><a href="../../posts/rust/how_to_create_yolov8_based_object_detection_web_service_using_python,_julia,_node.js,_javascript,_go_and_rust.html"><strong aria-hidden="true">7.25.</strong> How to create YOLOv8-based object detection web service using Python, Julia, Node.js, JavaScript, Go and Rust</a></li><li class="chapter-item "><a href="../../posts/rust/implment_builder_proc_macro_for_command_struct.html"><strong aria-hidden="true">7.26.</strong> implment Builder proc-macro for Command struct</a></li></ol></li><li class="chapter-item "><a href="../../posts/java/java.html"><strong aria-hidden="true">8.</strong> Java</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/java/java_grammar.html"><strong aria-hidden="true">8.1.</strong> java grammar and codewar</a></li><li class="chapter-item "><a href="../../posts/java/run_jar.html"><strong aria-hidden="true">8.2.</strong> java run .jar</a></li><li class="chapter-item "><a href="../../posts/java/java_pomxml_add_defaultgoal_to_build.html"><strong aria-hidden="true">8.3.</strong> Java pomxml add defaultGoal to build</a></li><li class="chapter-item "><a href="../../posts/java/java_set_mvn_mirror.html"><strong aria-hidden="true">8.4.</strong> Java set mvn mirror</a></li></ol></li><li class="chapter-item "><a href="../../posts/python/python.html"><strong aria-hidden="true">9.</strong> Python</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/python/convert_pesn.html"><strong aria-hidden="true">9.1.</strong> convert pesn</a></li><li class="chapter-item "><a href="../../posts/python/find_remove_dir.html"><strong aria-hidden="true">9.2.</strong> find and remove dir</a></li><li class="chapter-item "><a href="../../posts/python/timing_message.html"><strong aria-hidden="true">9.3.</strong> wechat send message</a></li><li class="chapter-item "><a href="../../posts/python/use_python_openpyxl_package_read_and_edit_excel_files.html"><strong aria-hidden="true">9.4.</strong> Use python openpyxl package read and edit excel files</a></li><li class="chapter-item "><a href="../../posts/python/sanctum_model_yaml.html"><strong aria-hidden="true">9.5.</strong> sanctum model yaml</a></li><li class="chapter-item "><a href="../../posts/python/how_to_detect_objects_on_images_using_the_yolov8_neural_network.html"><strong aria-hidden="true">9.6.</strong> How to detect objects on images using the YOLOv8 neural network</a></li><li class="chapter-item "><a href="../../posts/python/use_huggingface_model.html"><strong aria-hidden="true">9.7.</strong> use huggingface model</a></li></ol></li><li class="chapter-item "><a href="../../posts/go/go.html"><strong aria-hidden="true">10.</strong> Go</a></li><li class="chapter-item "><a href="../../posts/javascript/js.html"><strong aria-hidden="true">11.</strong> Javascript</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/javascript/js_tutorial.html"><strong aria-hidden="true">11.1.</strong> js tutorial</a></li><li class="chapter-item "><a href="../../posts/javascript/js_tutorial_map.html"><strong aria-hidden="true">11.2.</strong> ja map</a></li><li class="chapter-item "><a href="../../posts/javascript/js_tutorial_math.html"><strong aria-hidden="true">11.3.</strong> js math</a></li><li class="chapter-item "><a href="../../posts/javascript/js_tutorial_object.html"><strong aria-hidden="true">11.4.</strong> js object</a></li><li class="chapter-item "><a href="../../posts/javascript/js_tutorial_set.html"><strong aria-hidden="true">11.5.</strong> js set</a></li><li class="chapter-item "><a href="../../posts/javascript/single_thread_and_asynchronous.html"><strong aria-hidden="true">11.6.</strong> single thread and asynchronous</a></li><li class="chapter-item "><a href="../../posts/javascript/this.html"><strong aria-hidden="true">11.7.</strong> js this</a></li><li class="chapter-item "><a href="../../posts/javascript/js_implment_aes.html"><strong aria-hidden="true">11.8.</strong> js implment aes</a></li><li class="chapter-item "><a href="../../posts/javascript/getting_started_with_ajax.html"><strong aria-hidden="true">11.9.</strong> getting started with ajax</a></li><li class="chapter-item "><a href="../../posts/javascript/BinarySearchTree.html"><strong aria-hidden="true">11.10.</strong> binary search tree</a></li><li class="chapter-item "><a href="../../posts/javascript/goole_zx.html"><strong aria-hidden="true">11.11.</strong> goole zx</a></li><li class="chapter-item "><a href="../../posts/javascript/es6.html"><strong aria-hidden="true">11.12.</strong> es6</a></li></ol></li><li class="chapter-item "><a href="../../posts/ruby/ruby.html"><strong aria-hidden="true">12.</strong> Ruby</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ruby/rails_setup_env.html"><strong aria-hidden="true">12.1.</strong> ruby on rails setup environment</a></li><li class="chapter-item "><a href="../../posts/ruby/learn_ruby.html"><strong aria-hidden="true">12.2.</strong> learn ruby</a></li><li class="chapter-item "><a href="../../posts/ruby/ruby_note.html"><strong aria-hidden="true">12.3.</strong> Ruby Note</a></li><li class="chapter-item "><a href="../../posts/ruby/setup_ruby_for_ctf.html"><strong aria-hidden="true">12.4.</strong> Setup ruby for CTF</a></li></ol></li><li class="chapter-item "><a href="../../posts/react/react.html"><strong aria-hidden="true">13.</strong> React</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/react/react_life_cycle.html"><strong aria-hidden="true">13.1.</strong> react life cycle</a></li><li class="chapter-item "><a href="../../posts/react/react_router.html"><strong aria-hidden="true">13.2.</strong> react router</a></li><li class="chapter-item "><a href="../../posts/react/react_this.html"><strong aria-hidden="true">13.3.</strong> react this</a></li><li class="chapter-item "><a href="../../posts/react/react_interviw.html"><strong aria-hidden="true">13.4.</strong> react interview</a></li><li class="chapter-item "><a href="../../posts/react/important_react_interview.html"><strong aria-hidden="true">13.5.</strong> important react interview</a></li><li class="chapter-item "><a href="../../posts/react/react_quick_reference.html"><strong aria-hidden="true">13.6.</strong> react quick reference</a></li><li class="chapter-item "><a href="../../posts/react/redux_quick_reference.html"><strong aria-hidden="true">13.7.</strong> redux quick reference</a></li></ol></li><li class="chapter-item "><a href="../../posts/vue/vue.html"><strong aria-hidden="true">14.</strong> Vue</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/vue/vue_ajax.html"><strong aria-hidden="true">14.1.</strong> vue ajax</a></li></ol></li><li class="chapter-item "><a href="../../posts/angular/angular.html"><strong aria-hidden="true">15.</strong> Angular</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/angular/controller_communication.html"><strong aria-hidden="true">15.1.</strong> controller communication</a></li><li class="chapter-item "><a href="../../posts/angular/creating_custom_directives.html"><strong aria-hidden="true">15.2.</strong> creating custom directives</a></li><li class="chapter-item "><a href="../../posts/angular/directive_notes.html"><strong aria-hidden="true">15.3.</strong> directive notes</a></li><li class="chapter-item "><a href="../../posts/angular/directive_communication.html"><strong aria-hidden="true">15.4.</strong> directive communication</a></li><li class="chapter-item "><a href="../../posts/angular/post_params.html"><strong aria-hidden="true">15.5.</strong> post params</a></li><li class="chapter-item "><a href="../../posts/angular/read_json_angular.html"><strong aria-hidden="true">15.6.</strong> read json angular</a></li><li class="chapter-item "><a href="../../posts/angular/same_route_reload.html"><strong aria-hidden="true">15.7.</strong> same route reload</a></li></ol></li><li class="chapter-item "><a href="../../posts/css/css.html"><strong aria-hidden="true">16.</strong> Css</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/css/use_css_media.html"><strong aria-hidden="true">16.1.</strong> use css media</a></li></ol></li><li class="chapter-item "><a href="../../posts/php/php.html"><strong aria-hidden="true">17.</strong> Php</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/php/for_php_string_implment_some_extemtion_functions.html"><strong aria-hidden="true">17.1.</strong> for php string implment some extemtion functions</a></li><li class="chapter-item "><a href="../../posts/php/php_cheatsheet.html"><strong aria-hidden="true">17.2.</strong> PHP cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/windows/windows.html"><strong aria-hidden="true">18.</strong> Windows</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/windows/windows.html"><strong aria-hidden="true">18.1.</strong> Windows</a></li><li class="chapter-item "><a href="../../posts/windows/windows10_use_powershell_dedup_redundent_path.html"><strong aria-hidden="true">18.2.</strong> Windows10 use PowerShell dedup redundent PATH</a></li></ol></li><li class="chapter-item "><a href="../../posts/leetcode/leetcode.html"><strong aria-hidden="true">19.</strong> Leetcode</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/leetcode/rust_leetcode.html"><strong aria-hidden="true">19.1.</strong> rust leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_codewar.html"><strong aria-hidden="true">19.2.</strong> rust codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/swift_codewar.html"><strong aria-hidden="true">19.3.</strong> swift codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/js_leetcode.html"><strong aria-hidden="true">19.4.</strong> js leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/java_leetcode.html"><strong aria-hidden="true">19.5.</strong> java leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_huawei.html"><strong aria-hidden="true">19.6.</strong> huawei test</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_utils.html"><strong aria-hidden="true">19.7.</strong> rust common functions</a></li><li class="chapter-item "><a href="../../posts/leetcode/olympiad_training.html"><strong aria-hidden="true">19.8.</strong> Computer olympiad training</a></li></ol></li><li class="chapter-item "><a href="../../posts/ctf/CTF.html"><strong aria-hidden="true">20.</strong> CTF</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ctf/CTF_Note.html"><strong aria-hidden="true">20.1.</strong> CTF Note</a></li><li class="chapter-item "><a href="../../posts/ctf/0.1_Web.html"><strong aria-hidden="true">20.2.</strong> Web</a></li><li class="chapter-item "><a href="../../posts/ctf/4.1_Misc.html"><strong aria-hidden="true">20.3.</strong> Misc</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">20.4.</strong> PWN</a></li><li class="chapter-item "><a href="../../posts/ctf/3.1_Crypto.html"><strong aria-hidden="true">20.5.</strong> Crypto</a></li><li class="chapter-item "><a href="../../posts/ctf/3.4_RSA_note.html"><strong aria-hidden="true">20.6.</strong> Rsa attack</a></li><li class="chapter-item "><a href="../../posts/ctf/3.5_Base64.html"><strong aria-hidden="true">20.7.</strong> Base64</a></li><li class="chapter-item "><a href="../../posts/ctf/0.0_SQL Injection Cheatsheet.html"><strong aria-hidden="true">20.8.</strong> SQL Injection Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/ctf/1.1_SQL_injection.html"><strong aria-hidden="true">20.9.</strong> SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.2_SQL_injection_UNION_attacks.html"><strong aria-hidden="true">20.10.</strong> SQL Injection UNION attacks</a></li><li class="chapter-item "><a href="../../posts/ctf/1.3_Blind SQL injection.html"><strong aria-hidden="true">20.11.</strong> Blind SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.4_Code Injection.html"><strong aria-hidden="true">20.12.</strong> Code Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.5_SSRF.html"><strong aria-hidden="true">20.13.</strong> SSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.6_OS command injection.html"><strong aria-hidden="true">20.14.</strong> OS command injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.7_Local file inclusion.html"><strong aria-hidden="true">20.15.</strong> Local file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.8_Remote file inclusion.html"><strong aria-hidden="true">20.16.</strong> Remote file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.9_CSRFm.html"><strong aria-hidden="true">20.17.</strong> CSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.10_NoSQL injection.html"><strong aria-hidden="true">20.18.</strong> NoSQL injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.11_JSON injection.html"><strong aria-hidden="true">20.19.</strong> JSON injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.12_CTF_Web_SQL_Note.html"><strong aria-hidden="true">20.20.</strong> CTF Web SQL Note</a></li><li class="chapter-item "><a href="../../posts/ctf/2.1_XXE.html"><strong aria-hidden="true">20.21.</strong> XXE</a></li><li class="chapter-item "><a href="../../posts/ctf/2.2_XSS.html"><strong aria-hidden="true">20.22.</strong> XSS</a></li><li class="chapter-item "><a href="../../posts/ctf/2.3_Upload File.html"><strong aria-hidden="true">20.23.</strong> Upload File</a></li><li class="chapter-item "><a href="../../posts/ctf/2.4_serialize_unserialize.html"><strong aria-hidden="true">20.24.</strong> serialize unserialize</a></li><li class="chapter-item "><a href="../../posts/ctf/2.5_Race condition.html"><strong aria-hidden="true">20.25.</strong> Race condition</a></li><li class="chapter-item "><a href="../../posts/ctf/zip_plain_text_attack.html"><strong aria-hidden="true">20.26.</strong> Zip plain text attack</a></li><li class="chapter-item "><a href="../../posts/ctf/3.3_pwn HCTF2016 brop.html"><strong aria-hidden="true">20.27.</strong> pwn HCTF2016 brop</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_patch_defense_skill.html"><strong aria-hidden="true">20.28.</strong> PWN Patch defense skill</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_stack_overflow.html"><strong aria-hidden="true">20.29.</strong> PWN stack overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_heap_overflow.html"><strong aria-hidden="true">20.30.</strong> PWN heap overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_format_string_vulnerability.html"><strong aria-hidden="true">20.31.</strong> PWN Format String Vulnerability</a></li><li class="chapter-item "><a href="../../posts/ctf/kali_linux_tutorials.html"><strong aria-hidden="true">20.32.</strong> Kali linux tutorials</a></li><li class="chapter-item "><a href="../../posts/ctf/google_dorks_2023_lists.html"><strong aria-hidden="true">20.33.</strong> Google Dorks 2023 Lists</a></li><li class="chapter-item "><a href="../../posts/ctf/dvwa_writeup.html"><strong aria-hidden="true">20.34.</strong> DVWA WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/bwapp_writeup.html"><strong aria-hidden="true">20.35.</strong> bWAPP WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/sqlilabs_writeup.html"><strong aria-hidden="true">20.36.</strong> sqlilabs WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/pwnable_kr_challenge.html"><strong aria-hidden="true">20.37.</strong> Solutions for pwnable.kr</a></li><li class="chapter-item "><a href="../../posts/ctf/the_periodic_table.html"><strong aria-hidden="true">20.38.</strong> The Periodic Table</a></li><li class="chapter-item "><a href="../../posts/ctf/pwntools_cheatsheet.html"><strong aria-hidden="true">20.39.</strong> Pwntools Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/ctf/gdb_cheatsheet.html"><strong aria-hidden="true">20.40.</strong> GDB Cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/iltes/iltes.html"><strong aria-hidden="true">21.</strong> ILTES</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/iltes/iltes_writing.html"><strong aria-hidden="true">21.1.</strong> ILTES Writing</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <!-- Track and set sidebar scroll position -->
        <script>
            var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
            sidebarScrollbox.addEventListener('click', function(e) {
                if (e.target.tagName === 'A') {
                    sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
                }
            }, { passive: true });
            var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
            sessionStorage.removeItem('sidebar-scroll');
            if (sidebarScrollTop) {
                // preserve sidebar scroll position when navigating via links within sidebar
                sidebarScrollbox.scrollTop = sidebarScrollTop;
            } else {
                // scroll sidebar to current active section when navigating via "next/previous chapter" buttons
                var activeSection = document.querySelector('#sidebar .active');
                if (activeSection) {
                    activeSection.scrollIntoView({ block: 'center' });
                }
            }
        </script>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Andrew&#x27;s Blog</h1>

                    <div class="right-buttons">
                        <a href="https://gitee.com/dnrops/dnrops" title="Git repository" aria-label="Git repository">
                            <i id="git-repository-button" class="fa fa-github"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="swift-regular-exressions"><a class="header" href="#swift-regular-exressions">Swift regular exressions</a></h1>
<p style="display:flex;
    align-items: center;
    justify-content: end;
">Pub Date: 2023-07-25</p>
<p>Swift 5.7 中引入了正则表达式的语法支持，整理一下相关的一些话题、方法和示例，以备今后自己能够速查。</p>
<h2 id="总览"><a class="header" href="#总览">总览</a></h2>
<p>Swift 正则由标准库中的 <code>Regex</code> 类型驱动，需要 iOS 16.0 或 macOS 13.0，早期的 deploy 版本无法使用。</p>
<p>构建一个正则表达式的方式，分为传统的正则字面量构建，以及通过 Regex Builder DSL 的更加易读的方式。后者可以内嵌使用前者，以及其他一些已有的 parser，在可读性和功能上要强力很多。实践中，推荐<strong>结合使用字面量和 Builder API 在简洁和易读之间获取平衡</strong>。</p>
<h2 id="常见字面量"><a class="header" href="#常见字面量">常见字面量</a></h2>
<p>和其他各语言正则表达式的字面量没有显著不同。</p>
<p>直接将字面量包裹在 <code>/.../</code> 中使用，Swift 将把类似的声明转换为 <code>Regex</code> 类型的实例：</p>
<pre><code class="language-swift">let bitcoinAddress_v1 = /([13][a-km-zA-HJ-NP-Z0-9]{26,33})/
</code></pre>
<h3 id="字符集"><a class="header" href="#字符集">字符集</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th></tr></thead><tbody>
<tr><td><code>[aeiou]</code></td><td>匹配指定字符集</td><td>On<strong>e</strong>V’s␣D<strong>e</strong>n␣<strong>i</strong>s␣<strong>a</strong>␣bl<strong>o</strong>g.</td></tr>
<tr><td><code>[^aeiou]</code></td><td>排除字符集</td><td><strong>On</strong>e<strong>V’s␣D</strong>e<strong>n␣</strong>i<strong>s␣</strong>a<strong>␣bl</strong>o<strong>g.</strong></td></tr>
<tr><td><code>[A-Z]</code></td><td>匹配字符范围</td><td><strong>O</strong>ne<strong>V</strong>’s␣<strong>D</strong>en␣is␣a␣blog.</td></tr>
<tr><td><code>.</code></td><td>除换行符以外的任意字符。等效于 <code>[^\n\r]</code></td><td><strong>OneV’s␣Den␣is␣a␣blog.</strong></td></tr>
<tr><td><code>\s</code></td><td>匹配空格字符 (包括 tab 和换行)</td><td>OneV’s<strong>␣</strong>Den<strong>␣</strong>is<strong>␣</strong>a<strong>␣</strong>blog.</td></tr>
<tr><td><code>\S</code></td><td>匹配非空格字符</td><td><strong>OneV’s</strong>␣<strong>Den</strong>␣<strong>is</strong>␣<strong>a</strong>␣<strong>blog.</strong></td></tr>
<tr><td><code>[\s\S]</code></td><td>匹配空格和非空格，也即任意字符。等效于 <code>[^]</code></td><td><strong>OneV’s␣Den␣is␣a␣blog.</strong></td></tr>
<tr><td><code>\w</code></td><td>匹配字母数字下划线等低位 ASCII。等效于 <code>[A-Za-z0-9_]</code></td><td><strong>OneV</strong>‘<strong>s</strong>␣<strong>Den</strong>␣<strong>is</strong>␣<strong>a</strong>␣<strong>blog</strong>.</td></tr>
<tr><td><code>\W</code></td><td>等效于 <code>[^A-Za-z0-9_]</code></td><td> </td></tr>
<tr><td><code>\d</code></td><td>匹配数字，等效于 <code>[0-9]</code></td><td>+(<strong>81</strong>)<strong>021</strong>-<strong>1234</strong>-<strong>5678</strong></td></tr>
<tr><td><code>\D</code></td><td>非数字，等效于 <code>[^0-9]</code></td><td><strong>+(</strong> 81 <strong>)</strong> 021**-<strong>1234</strong>-**5678</td></tr>
</tbody></table>
</div>
<h3 id="数量"><a class="header" href="#数量">数量</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th><th>结果</th></tr></thead><tbody>
<tr><td><code>+</code></td><td>匹配一个或多个</td><td><code>b\w+</code></td><td>b <strong>be</strong> <strong>bee</strong> <strong>beer</strong> <strong>beers</strong></td></tr>
<tr><td><code>*</code></td><td>匹配零个或多个</td><td><code>b\w*</code></td><td><strong>b</strong> <strong>be</strong> <strong>bee</strong> <strong>beer</strong> <strong>beers</strong></td></tr>
<tr><td><code>{2,3}</code></td><td>匹配若干个</td><td><code>b\w{2,3}</code></td><td>b be <strong>bee</strong> <strong>beer</strong> <strong>beer</strong>s</td></tr>
<tr><td><code>?</code></td><td>匹配零个或一个</td><td><code>colou?r</code></td><td><strong>color</strong> <strong>colour</strong></td></tr>
<tr><td>数量 + <code>?</code></td><td>使前置数量进行惰性匹配 (尽可能少)</td><td><code>b\w+?</code></td><td>b <strong>be</strong> <strong>be</strong>e <strong>be</strong>er <strong>be</strong>ers</td></tr>
<tr><td><code>\|</code></td><td>逻辑或，择一匹配</td><td><code>b(a\|e\|i)d</code></td><td><strong>bad</strong> bud bod <strong>bed</strong> <strong>bid</strong></td></tr>
</tbody></table>
</div>
<h3 id="锚点"><a class="header" href="#锚点">锚点</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th><th>结果</th></tr></thead><tbody>
<tr><td><code>^</code></td><td>匹配字符串开头</td><td><code>^\w+</code></td><td><strong>she</strong> sells seashells</td></tr>
<tr><td><code>$</code></td><td>匹配字符串结尾</td><td><code>\w+$</code></td><td>she sells <strong>seashells</strong></td></tr>
<tr><td><code>\b</code></td><td>匹配 <code>\w</code> 和非 <code>\w</code> 的边缘位置</td><td><code>s\b</code></td><td>she sell<strong>s</strong> seashell<strong>s</strong></td></tr>
<tr><td><code>\B</code></td><td>匹配非边缘位置</td><td><code>s\B</code></td><td><strong>s</strong>he <strong>s</strong>ells <strong>s</strong>ea<strong>s</strong>hells</td></tr>
</tbody></table>
</div>
<h3 id="捕获组"><a class="header" href="#捕获组">捕获组</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th></tr></thead><tbody>
<tr><td><code>(OneV)+</code></td><td>捕获括号内的匹配，使其成组并出现在匹配结果中</td><td><strong>OneV</strong>’s Den is a blog.</td></tr>
<tr><td><code>(?&lt;name&gt;OneV)+</code></td><td>命名捕获匹配，在结果中可使用名字对匹配结果进行引用</td><td> </td></tr>
<tr><td><code>(?:OneV)+</code></td><td>成组但不进行捕获，允许使用数量但不关心和捕获结果</td><td> </td></tr>
</tbody></table>
</div>
<h3 id="lookahead"><a class="header" href="#lookahead">Lookahead</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th></tr></thead><tbody>
<tr><td><code>\d(?=px)</code></td><td><code>?=</code> - Positive lookahead。预先检查，符合时再进行主体匹配</td><td>1pt <strong>2</strong>px 3em <strong>4</strong>px</td></tr>
<tr><td><code>\d(?!px)</code></td><td><code>?!</code> - Negative lookahead。预先检查，不符合时进行主体匹配</td><td><strong>1</strong>pt 2px <strong>3</strong>em 4px</td></tr>
</tbody></table>
</div>
<h2 id="builder-dsl"><a class="header" href="#builder-dsl">Builder DSL</a></h2>
<p>字面量表达式虽然简洁，但是对应复杂情境会难以理解，也不便于修改。使用 <a href="https://developer.apple.com/documentation/regexbuilder"><code>RegexBuilder</code> 框架</a>提供的 DSL 来描述正则表达式是更具有表达性的方法。</p>
<p>比如，</p>
<pre><code class="language-swift">	let bitcoinAddress_v1 = /([13][a-km-zA-HJ-NP-Z0-9]{26,33})/
</code></pre>
<p>等效于：</p>
<pre><code class="language-swift">	
import RegexBuilder
let bitcoinAddress_v1 = Regex {
  Capture {
    One(.anyOf(&quot;13&quot;))
    Repeat(26...33) {
      CharacterClass(
        (&quot;a&quot;...&quot;k&quot;),
        (&quot;m&quot;...&quot;z&quot;),
        (&quot;A&quot;...&quot;H&quot;),
        (&quot;J&quot;...&quot;N&quot;),
        (&quot;P&quot;...&quot;Z&quot;),
        (&quot;0&quot;...&quot;9&quot;)
      )
    }
  }
}
</code></pre>
<p><code>Regex.init(_:)</code> 接受一个 result builder 形式的闭包，你可以往闭包中塞入多个 <code>RegexComponent</code> 来构建完整的正则表达式。注意 <code>Regex</code> 类型本身也满足 <code>RegexComponent</code> 协议，所以你也可以直接把字面量传递给 <code>Regex</code> 初始化方法。</p>
<p>字面量所提供的特性，在 Regex Builder 中都有对应。除此之外，Swift Regex Builder 框架还提供了更易读的强类型描述。一些常见的对应 <code>RegexComponent</code> 如下：</p>
<h3 id="字符集-1"><a class="header" href="#字符集-1">字符集</a></h3>
<p>字符集相关的 <code>RegexComponent</code> 基本被定义在 <a href="https://developer.apple.com/documentation/regexbuilder/characterclass"><code>CharacterClass</code></a> 中。</p>
<div class="table-wrapper"><table><thead><tr><th>字面量表达式</th><th>等效的 <code>RegexComponent</code></th></tr></thead><tbody>
<tr><td><code>[aeiou]</code></td><td><code>.anyOf(&quot;aeiou&quot;)</code>。为了可读性，可以考虑加上量词 <code>One(.anyOf(&quot;aeiou&quot;))</code></td></tr>
<tr><td><code>[^aeiou]</code></td><td><code>CharacterClass.anyOf(&quot;aeiou&quot;).inverted</code></td></tr>
<tr><td><code>[A-Z]</code></td><td><code>(&quot;A&quot;...&quot;Z&quot;)</code></td></tr>
<tr><td><code>.</code></td><td><code>.any</code></td></tr>
<tr><td><code>\s</code></td><td><code>.whitespace</code></td></tr>
<tr><td><code>\S</code></td><td><code>.whitespace.inverted</code></td></tr>
<tr><td><code>[\s\S]</code></td><td><code>CharacterClass(.whitespace, .whitespace.inverted)</code></td></tr>
<tr><td><code>\w</code></td><td><code>.word</code></td></tr>
<tr><td><code>\W</code></td><td><code>.word.inverted</code></td></tr>
<tr><td><code>\d</code></td><td><code>.digit</code></td></tr>
<tr><td><code>\D</code></td><td><code>.digit.inverted</code></td></tr>
</tbody></table>
</div>
<h3 id="数量-1"><a class="header" href="#数量-1">数量</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th><th>结果</th></tr></thead><tbody>
<tr><td><code>+</code></td><td>匹配一个或多个</td><td><code>b\w+</code></td><td>b <strong>be</strong> <strong>bee</strong> <strong>beer</strong> <strong>beers</strong></td></tr>
<tr><td><code>*</code></td><td>匹配零个或多个</td><td><code>b\w*</code></td><td><strong>b</strong> <strong>be</strong> <strong>bee</strong> <strong>beer</strong> <strong>beers</strong></td></tr>
<tr><td><code>{2,3}</code></td><td>匹配若干个</td><td><code>b\w{2,3}</code></td><td>b be <strong>bee</strong> <strong>beer</strong> <strong>beer</strong>s</td></tr>
<tr><td><code>?</code></td><td>匹配零个或一个</td><td><code>colou?r</code></td><td><strong>color</strong> <strong>colour</strong></td></tr>
<tr><td>数量 + <code>?</code></td><td>使前置数量进行惰性匹配 (尽可能少)</td><td><code>b\w+?</code></td><td>b <strong>be</strong> <strong>be</strong>e <strong>be</strong>er <strong>be</strong>ers</td></tr>
<tr><td><code>\|</code></td><td>逻辑或，择一匹配</td><td><code>b(a\|e\|i)d</code></td><td><strong>bad</strong> bud bod <strong>bed</strong> <strong>bid</strong></td></tr>
</tbody></table>
</div>
<h3 id="锚点-1"><a class="header" href="#锚点-1">锚点</a></h3>
<div class="table-wrapper"><table><thead><tr><th>表达式</th><th>说明</th><th>示例</th><th>结果</th></tr></thead><tbody>
<tr><td><code>^</code></td><td>匹配字符串开头</td><td><code>^\w+</code></td><td><strong>she</strong> sells seashells</td></tr>
<tr><td><code>$</code></td><td>匹配字符串结尾</td><td><code>\w+$</code></td><td>she sells <strong>seashells</strong></td></tr>
<tr><td><code>\b</code></td><td>匹配 <code>\w</code> 和非 <code>\w</code> 的边缘位置</td><td><code>s\b</code></td><td>she sell<strong>s</strong> seashell<strong>s</strong></td></tr>
<tr><td><code>\B</code></td><td>匹配非边缘位置</td><td><code>s\B</code></td><td><strong>s</strong>he <strong>s</strong>ells <strong>s</strong>ea<strong>s</strong>hells</td></tr>
</tbody></table>
</div>
<p>此外：</p>
<ul>
<li>对于多行匹配模式的情况 (如带有 <code>m</code> 的 <code>/^abc/m</code>)，此时 <code>^</code> 和 <code>$</code> 等效为 <code>.startOfLine</code>，<code>.endOfLine</code> 等。</li>
<li>对于 Unicode 支持，常用的还有 <code>.textSegmentBoundary (\y)</code> 等。</li>
</ul>
<h3 id="捕获"><a class="header" href="#捕获">捕获</a></h3>
<div class="table-wrapper"><table><thead><tr><th>字面量表达式</th><th>等效的 <code>RegexComponent</code></th></tr></thead><tbody>
<tr><td><code>(OneV)+</code></td><td><code>OneOrMore { Capture { &quot;OneV&quot; } }</code></td></tr>
<tr><td><code>(?&lt;name&gt;OneV)+</code></td><td><code>let name = Reference(Substring.self)</code> <code>OneOrMore { Capture(as: name) { &quot;OneV&quot; } }</code></td></tr>
<tr><td><code>(?:OneV)+</code></td><td><code>OneOrMore { &quot;OneV&quot; }</code></td></tr>
</tbody></table>
</div>
<p>Regex Builder 支持在 <code>Capture</code> 的过程中同时进行 mapping，把结果转换为其他形式的字符串甚至是其他类型的强类型值：</p>
<pre><code class="language-swift">Regex {
  TryCapture(as: kind) {
    OneOrMore(.word)
  } transform: {
    Transaction.Kind($0)
  } // 得到一个强类型 `Kind` 值
}
</code></pre>
<p>如果转换可能会失败并返回 <code>nil</code>，使用 <code>TryCapture</code>：失败时跳过匹配；如果转换一定会成功，使用普通的 <code>Capture</code>。</p>
<h3 id="lookahead-1"><a class="header" href="#lookahead-1">Lookahead</a></h3>
<div class="table-wrapper"><table><thead><tr><th>字面量表达式</th><th>等效的 <code>RegexComponent</code></th></tr></thead><tbody>
<tr><td><code>\d(?=px)</code></td><td><code>Regex { .digit ↵ Lookahead { &quot;px&quot; } }</code></td></tr>
<tr><td><code>\d(?!px)</code></td><td><code>Regex { .digit ↵ NegativeLookahead { &quot;px&quot; } }</code></td></tr>
</tbody></table>
</div>
<h2 id="常用-parser"><a class="header" href="#常用-parser">常用 Parser</a></h2>
<p>相对于字面量，使用 Regex Builder 的最大优势，在于可以嵌套使用已经存在的 Parser 进行匹配。凡是满足 <code>RegexComponent</code> 的值，都可以放到 <code>Regex</code> 表达式中。Foundation 中，部分 <code>ParseStrategy</code> 满足 <code>RegexComponent</code> 并提供相应方法来创建 <code>Regex</code> 中可用的 parser。iOS 16 中，默认可用 Parser 有：</p>
<div class="table-wrapper"><table><thead><tr><th>所属 Parser 类型</th><th>方法签名</th><th>可解析示例</th></tr></thead><tbody>
<tr><td><code>Date.ParseStrategy</code></td><td><code>date(_:locale:timeZone:calendar:)</code></td><td>Oct 21, 2015, 10/21/2015, etc</td></tr>
<tr><td><code>Date.ParseStrategy</code></td><td><code>date(format:locale:timeZone:calendar:twoDigitStartDate:)</code></td><td>05_04_22</td></tr>
<tr><td><code>Date.ParseStrategy</code></td><td><code>dateTime(date:time:locale:timeZone:calendar:)</code></td><td>10/17/2020, 9:54:29 PM</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601(timeZone:...)</code></td><td>2021-06-21T211015</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601Date(timeZone:dateSeparator:)</code></td><td>2015-11-14</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601WithTimeZone(...)</code></td><td>2021-06-21T21:10:15+0800</td></tr>
<tr><td><code>Decimal.FormatStyle.Currency</code></td><td><code>localizedCurrency(code:locale:)</code></td><td>$52,249.98 -&gt; <code>Decimal</code></td></tr>
<tr><td><code>Decimal.FormatStyle</code></td><td><code>localizedDecimal(locale:)</code></td><td>1.234, 1E5 -&gt; <code>Decimal</code></td></tr>
<tr><td><code>FloatingPointFormatStyle&lt;Double&gt;</code></td><td><code>localizedDouble(locale:)</code></td><td>1.234, 1E5 -&gt; -&gt; <code>Double</code></td></tr>
<tr><td><code>FlatingPointFormatStyle&lt;Double&gt;.Percent</code></td><td><code>localizedDoublePercentage(locale:)</code></td><td>15.4%, <code>-200%</code> -&gt; <code>Double</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;</code></td><td><code>localizedInteger(locale:)</code></td><td>199, 1.234 -&gt; <code>Int</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;.Currency</code></td><td><code>localizedIntegerCurrency(code:locale:)</code></td><td>$52,249.98 -&gt; <code>Int</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;.Percent</code></td><td><code>localizedIntegerPercentage(locale:)</code></td><td>15.4%, -200% -&gt; <code>Int</code></td></tr>
</tbody></table>
</div>
<blockquote>
<p>关于 Foundation 中 <code>ParseStrategy</code> 的相关内容，可以参看肘子兄的<a href="https://www.fatbobman.com/posts/newFormatter/">这篇博客</a>，以及 WWDC 21 中<a href="https://developer.apple.com/videos/play/wwdc2021/10109/">相关的视频</a>。</p>
</blockquote>
<h3 id="自定义-parser-和-customconsumingregexcomponent"><a class="header" href="#自定义-parser-和-customconsumingregexcomponent">自定义 Parser 和 <code>CustomConsumingRegexComponent</code></a></h3>
<p>对于自己实现的或是第三方提供的 Parser，可以通过满足 <code>CustomConsumingRegexComponent</code> 来让它进而满足 <code>RegexComponent</code> 并用在 Regex 构造中。</p>
<pre><code class="language-swift">
	
func consuming(     _ input: String,
    startingAt index: String.Index,
    in bounds: Range&lt;String.Index&gt;
) throws -&gt; (upperBound: String.Index, output: Self.RegexOutput)?
</code></pre>
<p>返回匹配停止时的上界，以及比配得到的结果本身即可。对于这一点，WWDC 22 的 <a href="https://developer.apple.com/videos/play/wwdc2022/110358">Swift Regex: Beyond the basics</a> 给了一个非常好的例子：</p>
<pre><code class="language-swift">	
import Darwin

struct CDoubleParser: CustomConsumingRegexComponent {
    typealias RegexOutput = Double

    func consuming(         _ input: String, startingAt index: String.Index, in bounds: Range&lt;String.Index&gt;
    ) throws -&gt; (upperBound: String.Index, output: Double)? {
        input[index...].withCString { startAddress in
            var endAddress: UnsafeMutablePointer&lt;CChar&gt;!
            let output = strtod(startAddress, &amp;endAddress)
            guard endAddress &gt; startAddress else { return nil }
            let parsedLength = startAddress.distance(to: endAddress)
            let upperBound = input.utf8.index(index, offsetBy: parsedLength)
            return (upperBound, output)
        }
    }
}
</code></pre>
<p>在很多情况下，我们可能会进一步地使用 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md">protocol 中泛型上下文静态查找</a>的特性，为 <code>RegexComponent</code> 添加类型成员，以便在 <code>Regex</code> 中直接使用：</p>
<pre><code class="language-swift">	
extension RegexComponent where Self == CDoubleParser {
    static var cDouble: Self { CDoubleParser() }
}
</code></pre>
<p>Foundation 中的各种 parser 基本都遵循了类似的实现方式。</p>
<h2 id="匹配方式"><a class="header" href="#匹配方式">匹配方式</a></h2>
<h3 id="常见的匹配方法"><a class="header" href="#常见的匹配方法">常见的匹配方法</a></h3>
<pre><code class="language-swift">	
// 匹配所有可能项，并将全部结果返回
input.matches(of: regex) // [Regex&lt;Output&gt;.Match]

// 匹配时返回第一个结果
input.firstMatch(of: regex) // Regex&lt;Output&gt;.Match?

// 整个字符串能完整匹配时才返回结果
input.wholeMatch(of: regex) // Regex&lt;Output&gt;.Match?

// 字符串的开始部分匹配的话返回结果
// 如果只需要判断是否匹配，使用 `start(with:)`
input.prefixMatch(of: regex) // Regex&lt;Output&gt;.Match?
</code></pre>
<p>匹配后得到的结果中，<code>.0</code> 返回匹配到的整个字符串，从 <code>.1</code> 开始是捕获的组：</p>
<pre><code class="language-swift">	
let regex = /Welcome to (.+?), a person blog from (\d+)/
let text = &quot;Welcome to OneV's Den, a person blog from 2011&quot;

if let result = text.wholeMatch(of: regex) {
    print(&quot;Title: (result.1)&quot;) // OneV's Den
    print(&quot;Year: (result.2)&quot;)  // 2011
}
</code></pre>
<p><code>Regex.Match</code> 实现了 dynamic lookup，可以使用 <code>Reference</code> 直接获取命名的捕获：</p>
<pre><code class="language-swift">	
let regex = /Welcome to (?&lt;name&gt;.+?), a person blog from (?&lt;year&gt;\d+)/
let text = &quot;Welcome to OneV's Den, a person blog from 2011&quot;

if let result = text.wholeMatch(of: regex) {
    print(&quot;Title: (result.name)&quot;) // OneV's Den
    print(&quot;Year: (result.year)&quot;)  // 2011
}
</code></pre>
<h3 id="基于-regex-的字符串算法操作"><a class="header" href="#基于-regex-的字符串算法操作">基于 Regex 的字符串算法/操作</a></h3>
<ul>
<li><code>input.ranges(of: regex)</code></li>
<li><code>input.replacing(regex, with: &quot;string&quot;)</code></li>
<li><code>input.trimmingPrefix(regex)</code></li>
</ul>
<p>等..原来在 <code>Collection</code> 中可以针对字符串的操作，可以找到对应的 <code>Regex</code> 版本。</p>
<h3 id="regex-变换flags"><a class="header" href="#regex-变换flags">Regex 变换/Flags</a></h3>
<p>在创建 <code>Regex</code> 后，可以使用其上的实例方法来对 <code>Regex</code> 进行部分修改。最常用的大概有：</p>
<ul>
<li><code>ignoresCase(_:)</code> - 匹配是否忽略大小写。等效于 <code>/[aeiou]/i</code> 中的 <code>i</code> flag。</li>
<li><code>anchorsMatchLineEndings(_:)</code> - <code>^</code> 和 <code>$</code> 是否也匹配每行。等效于 <code>/^[aeiou]$/m</code> 中的 <code>m</code> flag。</li>
<li><code>dotMatchesNewlines(_:)</code> - 字面量 <code>.</code> 是否应该匹配包括换行符在内的任意字符。等效于 <code>s</code> flag。</li>
</ul>
<h2 id="小结"><a class="header" href="#小结">小结</a></h2>
<p>Swift Regex 是符合 Swift 美学的正则写法，可以在标准库层面替代掉 Apple 平台上原有的被诟病已久的 <code>NSRegularExpression</code>。随着时代车轮的前行，<code>NSRegularExpression</code> 肯定将被逐渐扫进垃圾堆。</p>
<p>当前 Swift Regex 已经相对很完善了，它的优点非常明确：</p>
<ul>
<li>使用 DSL 的方式构建易于理解和维护的正则</li>
<li>可以与 Parser 结合使用，提供高质量的匹配</li>
</ul>
<p>当然，在本文写作时也还存在一些不足。</p>
<ul>
<li>文档不足，实际用例和社区支持也相对匮乏</li>
<li>需求的系统版本较高，近几年内可能难以完全迁移</li>
<li>不论字面量还是 DSL，暂时还不支持 <code>if</code> 等条件控制</li>
<li>Foundation 的 Parser 数量和种类不多</li>
</ul>
<p>不过这些毒点相对都是容易改善的，个人还是十分看好 Swift Regex 的前景。特别是用来做一些简单的文本处理和本地工具的话，会非常方便。</p>
<p>和 Swift <code>String</code> 一样，<code>Regex</code> 从设计初期就考虑了 Unicode 安全。不过本文暂时没有涉及 Unicode 的处理，日后如果用到再继续补充。</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../posts/swift/yinci_url.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../posts/swift/yinci_url.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>



        <script>
            window.playground_line_numbers = true;
        </script>

        <script>
            window.playground_copyable = true;
        </script>

        <script src="../../ace.js"></script>
        <script src="../../editor.js"></script>
        <script src="../../mode-rust.js"></script>
        <script src="../../theme-dawn.js"></script>
        <script src="../../theme-tomorrow_night.js"></script>

        <script src="../../elasticlunr.min.js"></script>
        <script src="../../mark.min.js"></script>
        <script src="../../searcher.js"></script>

        <script src="../../clipboard.min.js"></script>
        <script src="../../highlight.js"></script>
        <script src="../../book.js"></script>

        <!-- Custom JS scripts -->
        <script src="../../src/js/custom.js"></script>


    </div>
    </body>
</html>
